/*
 * Copyright (C) 2012 rothens
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package rpg.gfx.light;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import rpg.Location;
import rpg.entities.area.Renderable;
import rpg.maps.Map;
import rpg.maps.MapTile;

/**
 *
 * @author rothens
 */
public class Lighting {

	private static Lighting instance;
	
	public static final int MAX_DISTANCE = 8;
	private List<TileLight> openSet;
	private List<TileLight> closedSet;
	Map map;
	
	public synchronized static Lighting getInstance(){
		if(instance == null){
			instance = new Lighting();
		}
		return instance;
	}

	public Lighting() {

	}

	public class TileLight {

		public Location location;
		public int lightAmount;
		int xDiff;
		int yDiff;

		public TileLight(Location location, int lightAmount) {
			this.location = location;
			this.lightAmount = lightAmount;
		}
		
		public TileLight(Location location, int lightAmount, int xDiff, int yDiff){
			this(location,lightAmount);
			this.xDiff = xDiff;
			this.yDiff = yDiff;
		}
	}

	public void generateLights(List<LightSource> ls, Map m, Renderable r, int minLightLevel, int xDiff, int yDiff) {
		map = m;
		for (LightSource l : ls) {
			TileLight tl = new TileLight(l.loc, l.brightness);
			generateFromLightSource(tl);
		}
	}

	private void generateFromLightSource(TileLight from) {
		closedSet = new LinkedList<Lighting.TileLight>();
		openSet = new LinkedList<Lighting.TileLight>();
		closedSet.add(from);
		for (int i = 0; i < 4; ++i) {
			TileLight tl = getTile(from.location, i, from.lightAmount - 1);
			if (tl != null) {
				openSet.add(tl);
			}
		}
		while (!openSet.isEmpty()) {
			TileLight tl = openSet.get(0);
			openSet.remove(0);
			closedSet.add(tl);
			if (tl.lightAmount < 2) {
				continue;
			}
			for (int i = 0; i < 4; ++i) {
				TileLight ch = getTile(tl.location, i, tl.lightAmount - 1);
				if (ch == null) {
					continue;
				}
				TileLight old;
				if ((old = getFromClosed(ch.location)) != null) {
					if (ch.lightAmount > old.lightAmount) {
						old.lightAmount = ch.lightAmount;
						continue;
					} else {
						continue;
					}
				} else {
					openSet.add(ch);
				}
			}
		}
	}

	public TileLight getTile(Location location, int dir, int amt) {
		int tile = location.getTile(dir);
		if (tile == -1) {
			return null;
		}
		Location l = new Location(tile, map);
		MapTile mt = map.tiles[tile];
		if (!mt.isTransparent()) {
			return null;
		}
		TileLight ret = new TileLight(l, amt);
		if (getFromOpened(ret.location) != null) {
			return null;
		}

		return ret;

	}

	/**
	 * Tries to get a Node from the closedSet
	 *
	 * @param loc location to check
	 * @return Node, or null if not contained
	 */
	private TileLight getFromClosed(Location loc) {
		Iterator<TileLight> it = closedSet.iterator();
		TileLight ret = null;
		while (it.hasNext()) {

			ret = it.next();
			
			if (ret.location.equalTile(loc)) {
				return ret;
			}
		}
		return null;
	}

	/**
	 * Tries to get a Node from the openedSet
	 *
	 * @param loc location to check
	 * @return Node, or null if not contained
	 */
	private TileLight getFromOpened(Location loc) {
		Iterator<TileLight> it = openSet.iterator();
		TileLight ret = null;
		while (it.hasNext()) {

			ret = it.next();
			if (ret.location.equals(loc)) {
				return ret;
			}
		}
		return null;
	}
}
